package net.howson.phil.datahiding.old;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.FileImageInputStream;

import net.howson.phil.datahiding.stream.HeadedInputStream;

public class Test {

	private static final String EXTRACTOPTION = "-x";
	private static final String KEY = "-key";
	private static final String SEED = "-seed";
	private static final String OUTPUT = "-output";
	private static final String INPUT = "-input";
	private final File inputFile;
	private final File outputFile;
	private final File seedFile;

	public Test(String inputFile, String outputFile, String seedFile) {
		this.inputFile = new File(inputFile);
		this.outputFile = new File(outputFile);
		if (seedFile!=null) {
			this.seedFile = new File(seedFile);
		} else {
			this.seedFile = null;
		}
	}

	public static void main(String[] argv) throws IOException {
		String inputFile = null;
		String outputFile = null;
		String seedFile = null;
		int randomSeed = 0;
		boolean extract = false;

		for (int i = 0; i < argv.length; ++i) {
			if (INPUT.equals(argv[i])) {
				inputFile = argv[++i];
			} else if (OUTPUT.equals(argv[i])) {
				outputFile = argv[++i];
			} else if (SEED.equals(argv[i])) {
				seedFile = argv[++i];
			} else if (KEY.equals(argv[i])) {
				randomSeed = Integer.parseInt(argv[++i]);
			} else if (EXTRACTOPTION.equals(argv[i])) {
				extract = true;
			} else {
				usage();
				throw new IllegalArgumentException("Unknown argument : "
						+ argv[i]);
			}
		}
		if (inputFile==null) {
			usage();
			throw new IllegalArgumentException("Require an input file : " + inputFile);
		}
		if (outputFile==null) {
			usage();
			throw new IllegalArgumentException("Require an output file : " + outputFile);
		}
		if (!extract && seedFile==null) {
			usage();
			throw new IllegalArgumentException("Require a seed file : " + outputFile);
		}

		EncodingScheme scheme = new DefaultEncodingScheme2(randomSeed);
		Test app = new Test(inputFile, outputFile, seedFile);
		if (extract) {
			app.extract(scheme);
		} else {
			app.encode(scheme);
		}

	}

	private void encode(EncodingScheme scheme) throws IOException {

		BufferedImage seedImg = loadImage(seedFile);

		int width = seedImg.getWidth(null);
		int height = seedImg.getHeight(null);

		int encodableSize = scheme.getNumberOfBytesEncodable(width, height);
		long inputSize = inputFile.length();
		int repititions = (int) Math.ceil(((double) inputSize)
				/ (double) encodableSize);

		int seedX = 0;
		int seedY = 0;
		int offsetX = 0;

		BufferedImage outImg = new BufferedImage(width * repititions, height,
				BufferedImage.TYPE_3BYTE_BGR);

		InputStream is = new HeadedInputStream(inputSize, repititions,
				new BufferedInputStream(new FileInputStream(inputFile)));

		int i;
		while ((i = is.read()) != -1) {

			int rgb = seedImg.getRGB(seedX, seedY);
			int red = (rgb & 0x00ff0000) >>> 16;
			int green = (rgb & 0x0000ff00) >>> 8;
			int blue = rgb & 0x000000ff;

			outImg.setRGB(seedX + offsetX, seedY, scheme.encode(i, red, green,
					blue));

			if (++seedX >= width) {
				seedX = 0;
				if (++seedY >= height) {
					seedY = 0;
					offsetX += width;
				}
			}
		}

		Random random = new Random();
		while (seedY < height) {
			int rgb = seedImg.getRGB(seedX, seedY);
			int red = (rgb & 0x00ff0000) >>> 16;
			int green = (rgb & 0x0000ff00) >>> 8;
			int blue = rgb & 0x000000ff;
			outImg.setRGB(seedX + offsetX, seedY, scheme.encode(random
					.nextInt(256), red, green, blue));

			if (++seedX >= width) {
				seedX = 0;
				++seedY;
			}
		}

		is.close();

		String[] tmp = outputFile.getName().split("\\.");
		String outputExtension = tmp[tmp.length - 1];

		ImageIO.write(outImg, outputExtension, outputFile);

	}

	private BufferedImage loadImage(File inputFile)
			throws FileNotFoundException, IOException {
		String[] tmp = inputFile.getName().split("\\.");
		String inputSuffix = tmp[tmp.length - 1];

		for (Iterator<ImageReader> it = ImageIO
				.getImageReadersBySuffix(inputSuffix); it.hasNext();) {
			ImageReader reader = it.next();
			FileImageInputStream fileImageInputStream = new FileImageInputStream(
					inputFile);
			reader.setInput(fileImageInputStream);

			try {
				BufferedImage image = reader.read(0);
				if (image != null) {
					return image;
				}
			} catch (Exception ignored) {

			}
		}
		throw new IllegalArgumentException("Could not load image: " + inputFile);
	}

	private void extract(EncodingScheme scheme) throws IOException {
		BufferedImage inputImage = loadImage(inputFile);
		BufferedImage seedImage;
		if (seedFile!=null) {
			seedImage = loadImage(seedFile);
		} else {
			seedImage = null;
		}

		ByteBuffer header = ByteBuffer.allocate(8);
		for (int i = 0; i < 8; ++i) {
			int rgb = inputImage.getRGB(i, 0);
			int red = (rgb & 0x00ff0000) >>> 16;
			int green = (rgb & 0x0000ff00) >>> 8;
			int blue = rgb & 0x000000ff;
			int sred =-1; 
			int sgreen=-1;
			int sblue=-1; 
			
			if (seedImage!=null) {
				int rgb2 = seedImage.getRGB(i, 0);
				sred = (rgb2 & 0x00ff0000) >>> 16;
				sgreen = (rgb2 & 0x0000ff00) >>> 8;
				sblue = rgb2 & 0x000000ff;
			}
			
			int b = scheme.decode(red, green, blue, sred, sgreen, sblue);
			header.put((byte) b);
		}
		header.position(0);
		int size = header.getInt();
		int reps = header.getInt();

		int width = inputImage.getWidth() / reps;
		int height = inputImage.getHeight();

		int seedX = 8;
		int seedY = 0;
		int offsetX = 0;
		int read = 0;
		
		BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outputFile));
		
		while (read < size) {
			int rgb = inputImage.getRGB(seedX + offsetX, seedY);
			int red = (rgb & 0x00ff0000) >>> 16;
			int green = (rgb & 0x0000ff00) >>> 8;
			int blue = rgb & 0x000000ff;
			
			int sred = -1;
			int sgreen = -1;
			int sblue = -1;
			
			if (seedImage!=null) {
				int rgb2 = seedImage.getRGB(seedX, seedY);
				sred = (rgb2 & 0x00ff0000) >>> 16;
				sgreen = (rgb2 & 0x0000ff00) >>> 8;
				sblue = rgb2 & 0x000000ff;
			}
			

			out.write(scheme.decode(red, green, blue, sred, sgreen, sblue));
			++read;

			if (++seedX >= width) {
				seedX = 0;
				if (++seedY >= height) {
					seedY = 0;
					offsetX += width;
				}
			}
		}
		out.close();

	}

	private static void usage() {
		System.out.println("usage:");
		System.out.println("\t" + EXTRACTOPTION);
		System.out.println("\t\tExtract from image");
		System.out.println("\t" + KEY);
		System.out.println("\t\tInteger key");
		System.out.println("\t" + SEED);
		System.out.println("\t\tSeed image");
		System.out.println("\t" + OUTPUT);
		System.out.println("\t\tOutput file");
		System.out.println("\t" + INPUT);
		System.out.println("\t\tInput file");

	}

}